/**
 *    @file       ini_config.cpp
 *
 *    @brief      
 *    @details
 *
 *    @version    1.0
 *    @date       12/11/2012 10:52:11 PM
 *
 *    @author     Theophilus, wangtf418@gmail.com
 */
#include "ini_config.h"
#include "buffermgr.h"
#include "cfile.h"
#include "toolkit.h"

#define DEFAULT_INI_FILENAME "conf.ini"
#define MAX_INI_CONFFILE_SIZE 10485760

ini_config::ini_config()
	: m_filename(DEFAULT_INI_FILENAME)
{

}

ini_config::ini_config(const std::string &filename)
	: m_filename(filename)
{

}

ini_config::~ini_config()
{

}

bf_int_t ini_config::init()
{
	return load_config_file();
}

void ini_config::set_config_filename(const std::string &filename)
{
	m_filename = filename;
}

bf_int_t ini_config::read_item(const std::string &section, const std::string &key, const std::string &def_value, std::string &value_out)
{
	std::map<std::string, parameters_t>::iterator itr_sec = m_sections.find(section);
	if (itr_sec == m_sections.end())
	{
		value_out = def_value;
		return BF_OK;
	}
	parameters_t::iterator itr_para = itr_sec->second.find(key);
	if (itr_para == itr_sec->second.end())
	{
		value_out = def_value;
	}
	else
	{
		value_out = itr_para->second;
	}
	return BF_OK;
}

bf_int_t ini_config::write_item(const std::string &section, const std::string &key, const std::string &value)
{
	std::map<std::string, parameters_t>::iterator itr_sec = m_sections.find(section);
	if (itr_sec == m_sections.end())
	{
		parameters_t para_tmp;
		para_tmp.insert(make_pair(key, value));
		m_sections.insert(make_pair(section, para_tmp));
	}
	else
	{
		itr_sec->second[key] = value;
	}
	return dump_config_file();
}

bf_int_t ini_config::read_int_item(const std::string &section, const std::string &key, bf_int64_t def_value, bf_int64_t &value_out, bf_int64_t min_val /* = BF_INT64_MIN */, bf_int64_t max_val /* = BF_INT64_MAX */)
{
	std::string str_out;
	bf_int_t ret = read_item(section, key, "", str_out);
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	bf_int64_t value_tmp;
	if (str_out == "")
	{
		value_tmp = def_value;
	}
	else
	{
		value_tmp = strtoll(str_out.c_str(), NULL, 10);
	}

	if (value_tmp < min_val || value_tmp > max_val)
	{
		return BF_ERROR;
	}
	value_out = value_tmp;
	return BF_OK;
}

bf_int_t ini_config::write_int_item(const std::string &section, const std::string &key, bf_int64_t value)
{
	bf_char_t buf_tmp[30];
	snprintf(buf_tmp, sizeof(buf_tmp), "%lld", value);
	return write_item(section, key, buf_tmp);
}


bf_int_t ini_config::read_ip_item(const std::string &section, const std::string &key, const std::string &def_value, bf_uint_t &value_out)
{
	std::string str_value_out;
	bf_int_t ret = read_item(section, key, def_value, str_value_out);
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	ret = toolkit::str2inaddr(str_value_out, value_out);
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	value_out = bf_ntohl(value_out);
	return BF_OK;
}

bf_int_t ini_config::write_ip_item(const std::string &section, const std::string &key, bf_uint_t value)
{
	value = bf_htonl(value);
	std::string strip = toolkit::inaddr2str(value);
	return write_item(section, key, strip);
}


bf_int_t ini_config::load_config_file()
{
	if (m_filename.empty())
	{
		return BF_ERROR;
	}

	cfile conf_file(m_filename);
	bf_int_t ret = conf_file.open("r");
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	struct stat *file_stat = conf_file.stat();
	if (!file_stat)
	{
		return BF_ERROR;
	}
	bf_size_t file_size = file_stat->st_size;
	if (file_size >= MAX_INI_CONFFILE_SIZE)
	{
		return BF_ERROR;
	}
	bf_char_t *read_buf = new bf_char_t[file_size];
	buffermgr bufmgrtmp(read_buf, file_size);	
	ret = conf_file.read(read_buf, file_size);
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	conf_file.close();

	m_sections.clear();

	std::string cur_section;
	std::vector<bf_string_t> line_arr;
	toolkit::split_string_by_line(read_buf, file_size, line_arr);
	size_t line_num = line_arr.size();
   	for (size_t i = 0; i < line_num; i ++)
	{
		std::string str_tmp = line_arr[i].to_string();
		trim(str_tmp);
		if (str_tmp.empty())
		{
			continue;
		}
		if (str_tmp[0] == '[')
		{
			if (str_tmp.length() <= 2 || str_tmp[str_tmp.length() - 1] != ']')
			{
				return BF_ERROR;
			}
			std::string str_section = str_tmp.substr(1, str_tmp.length() - 2);
			toolkit::trim_string(str_section, " \t");
			if (str_section.empty() || m_sections.find(str_section) != m_sections.end())
			{
				return BF_ERROR;
			}
			parameters_t para_tmp;
			m_sections.insert(make_pair(str_section, para_tmp));
			cur_section = str_section;
			continue;
		}

		size_t pos = str_tmp.find_first_of("=");
		if (pos == std::string::npos)
		{
			return BF_ERROR;
		}
		std::string key = str_tmp.substr(0, pos);
		std::string value = str_tmp.substr(pos + 1);
		toolkit::trim_string(key, " \t");
		toolkit::trim_string(value, " \t");
		if (key.empty() || cur_section.empty() || m_sections[cur_section].find(key) != m_sections[cur_section].end())
		{
			return BF_ERROR;
		}
		m_sections[cur_section].insert(make_pair(key, value));
	}

#ifdef _BF_DEBUG_INICONFIG_	 
	std::map<std::string, parameters_t>::iterator itr_sec = m_sections.begin();
	for (; itr_sec != m_sections.end(); itr_sec ++)
	{
		printf("[%s:%d]\n", itr_sec->first.c_str(), (bf_int_t)itr_sec->first.length());
		parameters_t::iterator itr_para = itr_sec->second.begin();
		for (; itr_para != itr_sec->second.end(); itr_para ++)
		{
			printf("%s:%d=%s:%d\n", itr_para->first.c_str(), (bf_int_t)itr_para->first.length(), itr_para->second.c_str(), (bf_int_t)itr_para->second.length());
		}
	}
#endif

	return BF_OK;
}

bf_int_t ini_config::dump_config_file()
{
	if (m_filename.empty())
	{
		return BF_ERROR;
	}

	std::string conf_str = "";
	std::map<std::string, parameters_t>::iterator itr_sec = m_sections.begin();
	for (; itr_sec != m_sections.end(); itr_sec ++)
	{
		conf_str += "[" + itr_sec->first + "]" + "\n";
		parameters_t::iterator itr_para = itr_sec->second.begin();
		for (; itr_para != itr_sec->second.end(); itr_para ++)
		{
			conf_str += itr_para->first + "=" + itr_para->second + "\n";
		}
	}

	cfile conf_file(m_filename);
	bf_int_t ret = conf_file.open("w");
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	bf_size_t file_len = conf_str.length();
	ret = conf_file.write(conf_str.c_str(), file_len);
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	conf_file.close();
	return BF_OK;
}

void ini_config::trim(std::string &str)
{
	toolkit::trim_string(str, " \t");
	if (str[0] == ';' || str[0] == '#')
	{
		str.clear();
		return;
	}

	size_t pos = str.find_first_of(";");
	if (pos != std::string::npos)
	{
		str = str.substr(0, pos);
	}

	pos = str.find_first_of("#");
	if (pos != std::string::npos)
	{
		str = str.substr(0, pos);
	}
}



